classdef AnalogFilterDesign2 < handle
    
    properties     
        filterData;
        
        freqUnits = 'Hz';
        handles;
        
        polesDisp = [];
        zerosDisp = [];

        polesUsed = [];
        zerosUsed = [];
        poleStage = [];
        zeroStage = [];
        
        totalAttenuation;
        
        stagesList = {};
        selectedStage = 1;
        stagesPlotsOrigin = 1;
        
        passRange = [];
        
        gainVec = [];
        Hetsel = [];
        config = [];
        
        
        
        
        vMin = 10e-3;
        vMax = 13.5;
    end
    
    properties (SetObservable, AbortSet)
        actualGUI = 1;
    end
    
    methods
        function this = AnalogFilterDesign2
            hfig = hgload('FilterDesignGUI2.fig');
            this.handles = guihandles(hfig);
            movegui(hfig, 'center');
            scrPos = get(this.handles.mainMenu, 'Position');
            scrPos(2) = scrPos(2) + 15;
            set(this.handles.mainMenu, 'Position', scrPos);
            
            set(this.handles.designFilterBut, 'Callback', @(src, event) designFilterSelect(this, src, event));
            set(this.handles.backStageBut, 'Callback', @(src, event) backStageSelect(this, src, event));
            
            bg_image = imread('Images/attenPassImg.png');
            set(this.handles.attenPassBut, 'Callback', @(src, event) attenPassSelect(this, src, event), 'CData', bg_image);
            bg_image = imread('Images/zoomImg.png');
            set(this.handles.zoomInBut, 'Callback', @(src, event) zoomInSelect(this, src, event), 'CData', bg_image);
            bg_image = imread('Images/panImg.png');
            set(this.handles.panBut, 'Callback', @(src, event) panSelect(this, src, event), 'CData', bg_image);
            bg_image = imread('Images/cursorImg.png');
            set(this.handles.cursorBut, 'Callback', @(src, event) cursorSelect(this, src, event), 'CData', bg_image);
            bg_image = imread('Images/gridImg.png');
            set(this.handles.gridToggle, 'Callback', @(src, event) gridSelect(this, src, event), 'CData', bg_image);
            set(this.handles.logLinToggle, 'Callback', @(src, event) logLinSelect(this, src, event));
            set(this.handles.hzRadToggle, 'Callback', @(src, event) hzRadSelect(this, src, event));
            
            set(this.handles.individualBut, 'Callback', @(src, event) individualSelect(this, src, event));
            set(this.handles.acumulativeBut, 'Callback', @(src, event) acumulativeSelect(this, src, event));
            set(this.handles.superposeBut, 'Callback', @(src, event) superposeSelect(this, src, event));

            set(this.handles.createStageBut, 'Callback', @(src, event) createSelect(this, src, event));
            set(this.handles.autoPoleZeroBut, 'Callback', @(src, event) autoPZJoinSelect(this, src, event));
            set(this.handles.autoCascadeBut, 'Callback', @(src, event) autoCascadeSelect(this, src, event));
            
            set(this.handles.vmin, 'CallBack', @(src, event) vMinIn(this, src, event));
            set(this.handles.vmax2, 'CallBack', @(src, event) vMaxIn(this, src, event));
            
            set(this.handles.leftStagesBut, 'Callback', @(src, event) leftStagesSelect(this, src, event));
            set(this.handles.rightStagesBut, 'Callback', @(src, event) rightStagesSelect(this, src, event));
            
            set(this.handles.shiftLeftBut, 'Callback', @(src, event) shiftLeftSelect(this, src, event));
            set(this.handles.shiftRightBut, 'Callback', @(src, event) shiftRightSelect(this, src, event));
            set(this.handles.deleteStageBut, 'Callback', @(src, event) deleteStageSelect(this, src, event));
            
            set(this.handles.stage1But, 'Callback', @(src, event) stage1Select(this, src, event));
            set(this.handles.stage2But, 'Callback', @(src, event) stage2Select(this, src, event));
            set(this.handles.stage3But, 'Callback', @(src, event) stage3Select(this, src, event));
            set(this.handles.stage4But, 'Callback', @(src, event) stage4Select(this, src, event));
            set(this.handles.stage5But, 'Callback', @(src, event) stage5Select(this, src, event));
            
            set(this.handles.polesList, 'Callback', @(src, event) listSelect(this, src, event));
            set(this.handles.zerosList, 'Callback', @(src, event) listSelect(this, src, event));
            
            set(this.handles.Gi, 'Callback', @(src, event) setGainSelect(this, src, event));
            
        end
        
        function setFilterData(this, filterData)
            %%%%%%%%%%%%%%%%% Restart Data %%%%%%%%%%%%%%%%%%%%%
            this.polesDisp = [];
            this.zerosDisp = [];
            this.polesUsed = [];
            this.zerosUsed = [];
            this.stagesList = {};
            this.cleanInformation();
            hold(this.handles.poleZeroAxes, 'off');
            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
            this.filterData = filterData;
            
            j = 1;
            for i = 1:length(this.filterData.filterCalc.Poles)
                if imag(this.filterData.filterCalc.Poles(i)) >= 0
                    this.polesDisp(j) = this.filterData.filterCalc.Poles(i);
                    j = j+1;
                end
            end
            j = 1;
            for i = 1:length(filterData.filterCalc.Zeros)
                if imag(this.filterData.filterCalc.Zeros(i)) >= 0
                    this.zerosDisp(j) = this.filterData.filterCalc.Zeros(i);
                    j = j+1;
                end
            end

            this.refreshPolesZerosList();
            this.cleanStagesBar();
            
            Fp = this.filterData.Fp;
            switch this.filterData.filterType
                case 'Lowpass'
                    this.passRange = logspace(log10(Fp/100), log10(Fp), 5000);
                case 'Highpass'
                    this.passRange = logspace(log10(Fp), log10(Fp*100), 5000);
                case 'Bandpass'
                    this.passRange = logspace(log10(Fp(1)), log10(Fp(2)), 5000);
                case 'Stopband'
                    this.passRange = [logspace(log10(Fp(1)/100), log10(Fp(1)), 2500), logspace(log10(Fp(2)), log10(Fp(2)*100), 2500)];
                case 'Group-Delay'
                    this.passRange = logspace(log10(Fp/100), log10(Fp), 5000);
            end
            this.passRange = 2 * pi * this.passRange;
        end
        
        function visible(this, value)
            if value == 2
                set(this.handles.mainMenu, 'Visible', 'On');
            else
                set(this.handles.mainMenu, 'Visible', 'Off');
            end
        end
        
        function this = designFilterSelect(this, ~, ~)
            if isempty(this.polesDisp)
                this.actualGUI = 3 + rand(1,1)/2;
            else
                errordlg('Error: There are poles left', 'Inconsistent error');
                return
            end
        end
        
        function this = backStageSelect(this, ~, ~)
            this.actualGUI = 1 + rand(1,1)/2;
        end
        
        function this = attenPassSelect(this, ~, ~)
            if get(this.handles.acumulativeBut, 'Value')
                this.refreshSelectedStage();
            end
        end
        
        function this = zoomInSelect(this, ~, ~)
            zoom;
        end
        
        function this = panSelect(this, ~, ~)
            pan;
        end
        
        function this = cursorSelect(this, ~, ~)
            datacursormode;
        end
        
        function this = gridSelect(this, src, ~)
            if get(src, 'Value')
                grid(this.handles.axesPrincipal, 'On')
            else
                grid(this.handles.axesPrincipal, 'Off')
            end
        end
        
        function this = logLinSelect(this, src, ~)
            if get(src, 'Value')
                set(this.handles.axesPrincipal,'XScale','lin');
            else
                set(this.handles.axesPrincipal,'XScale','log');
            end
        end
        
        function this = hzRadSelect(this, src, ~)
            if get(src, 'Value')
                this.freqUnits = 'rad/s';
                set(this.handles.Hz1, 'String', 'rad/s');
                set(this.handles.Hz2, 'String', 'rad/s');
            else
                this.freqUnits = 'Hz';
                set(this.handles.Hz1, 'String', 'Hz');
                set(this.handles.Hz2, 'String', 'Hz');
            end
            
            this.refreshPolesZerosList();
            this.refreshSelectedStage();
        end
        
        function this = individualSelect(this, src, ~)
            set(src, 'Value', 1);
            this.refreshSelectedStage();
        end
        
        function this = acumulativeSelect(this, src, ~)
            set(src, 'Value', 1);
            this.refreshSelectedStage();
        end
        
        function this = superposeSelect(this, src, ~)
            set(src, 'Value', 1);
            this.refreshSelectedStage();
        end
        
        function this = createSelect(this, ~, ~)
            polesListVal = get(this.handles.polesList, 'Value');
            zerosListVal = get(this.handles.zerosList, 'Value');
            
            if isempty(polesListVal)
                errordlg('Error: There are no poles selected', 'Inconsistent error');
                return
            end
            
            if ~isempty(this.polesDisp)
                if isempty(zerosListVal)
                    errordlg('Error: There are no zeros selected', 'Inconsistent error');
                    return
                end

                totalPoles = [this.polesDisp' this.polesUsed']';
                selectedPoles = totalPoles(polesListVal);

                totalZeros = [this.zerosDisp' this.zerosUsed']';
                ZSel = [zerosListVal(zerosListVal <= length(this.zerosDisp)) (zerosListVal(zerosListVal > (length(this.zerosDisp)+1)))-1];
                selectedZeros = totalZeros(ZSel);

                if (length(polesListVal) == 2) && (sum(imag(selectedPoles) ~= 0))
                    errordlg('Error: Single pole can be conjugated', 'Inconsistent error');
                    return
                end

                if sum(polesListVal > length(this.polesDisp))
                    errordlg('Error: Pole already used', 'Inconsistent error');
                    return
                end

                if length(polesListVal) > 2
                    errordlg('Error: The number of poles can''t be more than 2', 'Inconsistent error');
                    return
                end

                if (length(zerosListVal) > 1) && (length(selectedZeros) ~= length(zerosListVal))
                    errordlg('Error: Incorrect selected zero value', 'Inconsistent error');
                    return
                end

                if length(zerosListVal) > 2
                    errordlg('Error: The number of zeros can''t be more than 2', 'Inconsistent error');
                    return
                end

                if sum(zerosListVal) > (length(this.zerosDisp)+1)
                    errordlg('Error: There is a zero already used', 'Inconsistent error');
                    return
                end

                polesLeft = 0;
                polesInStage = 0;
                for k = 1:length(this.polesDisp)
                    if imag(this.polesDisp(k)) > 0
                        if k ~= polesListVal
                            polesLeft = polesLeft + 2;
                        else
                            polesInStage = polesInStage + 2;
                        end
                    else
                        if k ~= polesListVal
                            polesLeft = polesLeft + 1;
                        else
                            polesInStage = polesInStage + 1;
                        end
                    end
                end
                zerosLeft = 0;
                zerosInStage = 0;
                for k = 1:length(this.zerosDisp)
                    if imag(this.zerosDisp(k)) > 0
                        if k ~= zerosListVal
                            zerosLeft = zerosLeft + 2;
                        else
                            zerosInStage = zerosInStage + 2;
                        end
                    else
                        if k ~= zerosListVal
                            zerosLeft = zerosLeft + 1;
                        else
                            zerosInStage = zerosInStage + 1;
                        end
                    end
                end

                if polesInStage < zerosInStage
                    errordlg('Error: The number of zeros selected can''t be higher than the number of poles selected', 'Inconsistent error');
                    return
                end

                if polesLeft < zerosLeft
                    errordlg('Error: The number of poles left can''t be less than the number of zeros left', 'Inconsistent error');
                    return
                end

                for k = length(polesListVal):-1:1
                    this.polesUsed(end+1) = this.polesDisp(polesListVal(k));
                    this.polesDisp(polesListVal(k)) = [];
                end

                if (length(zerosListVal) == 1) && (zerosListVal == length(this.zerosDisp)+1)
                    zerosListVal = [];
                    selectedZeros = [];
                end

                for k = length(zerosListVal):-1:1
                    this.zerosUsed(end+1) = this.zerosDisp(zerosListVal(k));
                    this.zerosDisp(zerosListVal(k)) = [];
                end
                this.stagesList{end+1} = SecondOrdStage(allPoles(selectedPoles), allZeros(selectedZeros), pole2Q(selectedPoles), this.filterData.filterCalc.Frec);
            else
                this.stagesList{end+1} = SecondOrdStage([], [], [], this.filterData.filterCalc.Frec);
                warndlg('Gain stage created', 'Additional stage');
            end
            
            if isempty(this.polesDisp)
                this.suitGains();
            end
            
            this.selectedStage = length(this.stagesList);
            this.stagesPlotsOrigin = length(this.stagesList) - 4;
            
            this.refreshPolesZerosList();
            this.calcTotalAttenuation();
            this.refreshSelectedStage();
            this.refreshStagesBar(true);
            this.refreshDynamicRange();
            
        end
        
        function autoPZJoinSelect(this, ~, ~)
            pairedStages  = autoJoinPZ(this.polesDisp, this.zerosDisp);
            for i = 1:length(pairedStages)
                this.stagesList{end+1} = SecondOrdStage([pairedStages(i).polo1 pairedStages(i).polo2],...
                [pairedStages(i).zero1 pairedStages(i).zero2], pole2Q(pairedStages(i).polo1), this.filterData.filterCalc.Frec);
            end
            
            for i = 1:length(this.polesDisp)
                this.polesUsed(end+1) = this.polesDisp(i);
            end
            for i = 1:length(this.zerosDisp)
                this.zerosUsed(end+1) = this.zerosDisp(i);
            end
            
            this.polesDisp = [];
            this.zerosDisp = [];
            
            this.selectedStage = length(this.stagesList);
            this.stagesPlotsOrigin = length(this.stagesList) - 4;

            this.refreshPolesZerosList();
            this.refreshSelectedStage();
            this.refreshStagesBar(true);
            this.calcTotalAttenuation();
            this.refreshDynamicRange();
            this.suitGains();
        end
        
        function autoCascadeSelect(this, src, ~)
%             if isempty(this.Hetsel)
                if isempty(this.polesDisp) && isempty(this.zerosDisp)
                    for i = 1:length(this.stagesList)
                        vectorTF(i) = this.stagesList{i}.transferFunction;
                    end
                    signalVal = get(this.handles.signalLevelMenu, 'Value');
                    [this.gainVec, dinRange, minvec, maxvec, this.config, this.Hetsel] = autoCascade(vectorTF, this.passRange);
                    stagesList2 = cell(length(this.stagesList), 1);
                    for k = 1:length(this.stagesList)
                        stagesList2{k} = this.stagesList{(this.config(signalVal, :) == k)};
                        stagesList2{k}.transferFunction = this.Hetsel(signalVal, ((this.config(signalVal, :) == k)));
                        stagesList2{k}.setGain(this.gainVec(signalVal,k)*this.stagesList{((this.config(signalVal, :) == k))}.Gi, false);
                        [auxAttenuation ~] = bode(stagesList2{k}.transferFunction, this.stagesList{k}.freq*(2*pi));
                        stagesList2{k}.attenuation = -20*log10(squeeze(auxAttenuation));
                    end
                    this.stagesList = stagesList2';
                end

                this.calcTotalAttenuation();
                this.refreshSelectedStage();
                this.refreshStagesBar(false);
                this.refreshDynamicRange();
%             end
            
        end
        
        function vMinIn(this, src, event)
            vmin = str2compnum(get(src, 'String'));
            vmax = str2compnum(get(this.handles.vmax2, 'String'));
            if isnan(vmin) || (vmin < 1e-3) || (vmin > 10)
                errordlg('Error: Vmin must be a numeric value between 1mV and 10V', 'Inconsistent error');
                set(src, 'String', num2compstr(this.vMin, 1));
                return;
            end
            if vmin > vmax
                errordlg('Error: Vmin must be less than Vmax', 'Inconsistent error');
                set(src, 'String', num2compstr(this.vMin, 1));
                return;
            end 
            this.vMin = vmin;
            if ~isempty(this.stagesList)
                PRD = dinrange(this.stagesList{this.selectedStage}.transferFunction, this.passRange, 1);
                drange = 20*log10(vmax/vmin) - 20*log10(PRD);
                this.stagesList{this.selectedStage}.dinRang = drange;
                set(this.handles.DR, 'String', num2str(round(drange*10)/10));
            end
        end
        
        function vMaxIn(this, src, event)
            vmax = str2compnum(get(src, 'String'));
            vmin = str2compnum(get(this.handles.vmin, 'String'));
            if isnan(vmax) || (vmax < 1) || (vmax > 36)
                errordlg('Error: Vmax must be a numeric value between 1V and 30V', 'Inconsistent error');
                set(src, 'String', num2compstr(this.vMax, 1));
                return;
            end
            if vmax < vmin
                errordlg('Error: Vmax must be higher than Vmin', 'Inconsistent error');
                set(src, 'String', num2compstr(this.vMin, 1));
                return;
            end 
            this.vMax = vmax;
            if ~isempty(this.stagesList)
                PRD = dinrange(this.stagesList{this.selectedStage}.transferFunction, this.passRange, 1);
                drange = 20*log10(vmax/vmin) - 20*log10(PRD);
                this.stagesList{this.selectedStage}.dinRang = drange;
                set(this.handles.DR, 'String', num2str(round(drange*10)/10));
            end
        end
        
        function setGainSelect(this, src, ~)
            gainNum = 10^(str2double(get(src, 'String'))/20);
            if isempty(this.stagesList)
                errordlg('Error: There is no stage selected', 'Inconsistent error');
                return;
            end
            if isnan(gainNum)
                errordlg('Error: Gain must be a numeric value(dB)', 'Inconsistent error');
                return;
            end
            this.stagesList{this.selectedStage}.setGain(gainNum, true);
            this.calcTotalAttenuation();
            this.refreshSelectedStage();
        end
        
        function refreshSelectedStage(this)
            if ~isempty(this.stagesList)
                if strcmp(this.freqUnits, 'Hz')
                    mult = 1;
                else
                    mult = 2*pi;
                end
                
                set(this.handles.Gi, 'String', sprintf('%.2f', 20*log10(this.stagesList{this.selectedStage}.Gi)));
                
                if ~isnan(this.stagesList{this.selectedStage}.Q) 
                    set(this.handles.Q, 'String', sprintf('%.2f', this.stagesList{this.selectedStage}.Q));
                else
                    set(this.handles.Q, 'String', 'None');
                end
                if ~isnan(this.stagesList{this.selectedStage}.Finf)
                    set(this.handles.Hz1, 'Visible', 'On');
                    set(this.handles.Finf, 'String', sprintf('%.2f', this.stagesList{this.selectedStage}.Finf*mult));
                else
                    set(this.handles.Hz1, 'Visible', 'Off');
                    set(this.handles.Finf, 'String', 'None');
                end
                if ~isnan(this.stagesList{this.selectedStage}.F0)
                    set(this.handles.Hz2, 'Visible', 'On');
                    set(this.handles.F0, 'String', sprintf('%.2f', this.stagesList{this.selectedStage}.F0*mult));
                else
                    set(this.handles.Hz2, 'Visible', 'Off');
                    set(this.handles.F0, 'String', 'None');
                end
                set(this.handles.DR, 'String', sprintf('%.2f', this.stagesList{this.selectedStage}.dinRang));
                
                hold(this.handles.axesPrincipal, 'off');

                if strcmp(this.freqUnits, 'rad/s')
                    freq = this.stagesList{this.selectedStage}.freq * 2 * pi;
                else
                    freq = this.stagesList{this.selectedStage}.freq;
                end
                a = find(freq > freq(1)*10, 1, 'first');
                b = find(freq > freq(end)/10, 1,'first');
                yMin = 10000;
                yMax = -10000;
                
                if get(this.handles.individualBut, 'Value')
                    semilogx(this.handles.axesPrincipal, freq, this.stagesList{this.selectedStage}.attenuation);
                    yMin = min(this.stagesList{this.selectedStage}.attenuation(a:b));
                    yMax = max(this.stagesList{this.selectedStage}.attenuation(a:b));
                    title(this.handles.axesPrincipal, strcat({'Stage '}, num2str(this.selectedStage)), 'FontSize', 11);
                elseif get(this.handles.acumulativeBut, 'Value')
                    semilogx(this.handles.axesPrincipal, freq, this.totalAttenuation);
                    yMin = min(this.totalAttenuation(a:b));
                    yMax = max(this.totalAttenuation(a:b));
                    title(this.handles.axesPrincipal, 'Acumulative Attenuation', 'FontSize', 11);
                    if get(this.handles.attenPassBut, 'Value')
                        drawAttenPass(this.filterData, this.handles.axesPrincipal, this.freqUnits);
                    end
                else
                    for i = 1:length(this.stagesList)
                       semilogx(this.handles.axesPrincipal, freq, this.stagesList{i}.attenuation);
                       if min(this.stagesList{i}.attenuation(a:b)) < yMin
                           yMin = min(this.stagesList{i}.attenuation(a:b));
                       end
                       if max(this.stagesList{i}.attenuation(a:b)) > yMax
                           yMax = max(this.stagesList{i}.attenuation(a:b));
                       end
                       hold(this.handles.axesPrincipal, 'all');
                       title(this.handles.axesPrincipal, 'Superposed Stages', 'FontSize', 11);
                    end
                end
                
                xlim(this.handles.axesPrincipal, [freq(1)*10, freq(end)/10]);
                if yMin ~= yMax
                    ylim(this.handles.axesPrincipal, [yMin yMax]);
                else
                    ylim(this.handles.axesPrincipal, [yMin-20 yMax+20]);
                end
                xlabel(this.handles.axesPrincipal, strcat('Frequency [', this.freqUnits, ']'), 'FontSize', 11);
                ylabel(this.handles.axesPrincipal, 'Attenuation [dB]', 'FontSize', 11);
                this.gridSelect(this.handles.gridToggle, 0);
            else
                this.cleanInformation();
            end
            if ~isempty(this.stagesList)
                PRD = dinrange(this.stagesList{this.selectedStage}.transferFunction, this.passRange, 1);
                drange = 20*log10(this.vMax/this.vMin) - 20*log10(PRD);
                this.stagesList{this.selectedStage}.dinRang = drange;
                set(this.handles.DR, 'String', num2str(round(drange*10)/10));
            end
        end
        
        function refreshStagesBar(this, added)
            this.cleanStagesBar();
            if ~isempty(this.stagesList)
                if length(this.stagesList) <= 5
                    numPlots = length(this.stagesList);
                    this.stagesPlotsOrigin = 1;
                else
                    numPlots = 5;
                    if added && numPlots > 6
                        this.stagesPlotsOrigin = this.stagesPlotsOrigin + 1;
                    end
                    if this.stagesPlotsOrigin > 1
                        set(this.handles.leftStagesBut, 'Enable', 'On');
                    end
                    if (length(this.stagesList) - this.stagesPlotsOrigin) >= 5
                        set(this.handles.rightStagesBut, 'Enable', 'On');
                    end
                end

                for i = 1:numPlots
                    switch i
                        case 1
                            hP = this.handles.axes1;
                            but = this.handles.stage1But;
                        case 2
                            hP = this.handles.axes2;
                            but = this.handles.stage2But;
                        case 3
                            hP = this.handles.axes3;
                            but = this.handles.stage3But;
                        case 4
                            hP = this.handles.axes4;
                            but = this.handles.stage4But;
                        case 5
                            hP = this.handles.axes5;
                            but = this.handles.stage5But;
                    end

                    semilogx(hP, this.stagesList{i+this.stagesPlotsOrigin-1}.freq, this.stagesList{i+this.stagesPlotsOrigin-1}.attenuation);
                    xlim(hP, [this.stagesList{i+this.stagesPlotsOrigin-1}.freq(1)*10, this.stagesList{i+this.stagesPlotsOrigin-1}.freq(end)/10]);
                    removeAxis(hP);
                    set(but, 'String', strcat({'Stage '}, num2str(i+this.stagesPlotsOrigin-1)), 'Visible', 'On');
                    if this.selectedStage == (i+this.stagesPlotsOrigin-1)
                        set(but, 'Value', 1)
                    else
                        set(but, 'Value', 0)
                    end
                    yMin = min(this.stagesList{i+this.stagesPlotsOrigin-1}.attenuation);
                    yMax = max(this.stagesList{i+this.stagesPlotsOrigin-1}.attenuation);
                    if abs(yMin - yMax) < 1e-12
                        ylim(hP, [yMin-20, yMin+20]);
                    end
                    this.gridSelect(this.handles.gridToggle, 0);
                end
            end
        end
        
        function cleanStagesBar(this)
            cla(this.handles.axes1);
            cla(this.handles.axes2);
            cla(this.handles.axes3);
            cla(this.handles.axes4);
            cla(this.handles.axes5);
            set(this.handles.axes1, 'Visible', 'Off');
            set(this.handles.axes2, 'Visible', 'Off');
            set(this.handles.axes3, 'Visible', 'Off');
            set(this.handles.axes4, 'Visible', 'Off');
            set(this.handles.axes5, 'Visible', 'Off');
            set(this.handles.stage1But, 'Visible', 'Off', 'Value', 0);
            set(this.handles.stage2But, 'Visible', 'Off', 'Value', 0);
            set(this.handles.stage3But, 'Visible', 'Off', 'Value', 0);
            set(this.handles.stage4But, 'Visible', 'Off', 'Value', 0);
            set(this.handles.stage5But, 'Visible', 'Off', 'Value', 0);
            set(this.handles.leftStagesBut, 'Enable', 'Off');
            set(this.handles.rightStagesBut, 'Enable', 'Off');
        end
        
        function cleanInformation(this)
            cla(this.handles.axesPrincipal);
            set(this.handles.Gi, 'String', '');
            set(this.handles.Q, 'String', '');
            set(this.handles.Finf, 'String', '');
            set(this.handles.F0, 'String', '');
            set(this.handles.DR, 'String', '');
            set(this.handles.TDR, 'String', '');
            set(this.handles.individualBut, 'Value', 1);
            set(this.handles.acumulativeBut, 'Value', 0);
            set(this.handles.superposeBut, 'Value', 0);
        end
            
        function this = leftStagesSelect(this, ~, ~)
            this.stagesPlotsOrigin = this.stagesPlotsOrigin - 1;
            this.refreshStagesBar(false);
        end
        
        function this = rightStagesSelect(this, ~, ~)
            this.stagesPlotsOrigin = this.stagesPlotsOrigin + 1;
            this.refreshStagesBar(false);
        end
        
        function this = shiftLeftSelect(this, ~, ~)
            if this.selectedStage > 1
                auxStage = this.stagesList{this.selectedStage};
                this.stagesList{this.selectedStage} = this.stagesList{this.selectedStage - 1};
                this.stagesList{this.selectedStage - 1} = auxStage;
                this.selectedStage = this.selectedStage - 1;
                if this.selectedStage < this.stagesPlotsOrigin
                    this.stagesPlotsOrigin = this.stagesPlotsOrigin - 1;
                end
                
                this.refreshSelectedStage();
                this.refreshStagesBar(false);
                this.refreshDynamicRange();
            end
        end
        
        function this = shiftRightSelect(this, ~, ~)
            if this.selectedStage < length(this.stagesList)
                auxStage = this.stagesList{this.selectedStage};
                this.stagesList{this.selectedStage} = this.stagesList{this.selectedStage + 1};
                this.stagesList{this.selectedStage + 1} = auxStage;
                this.selectedStage = this.selectedStage + 1;
                if this.selectedStage > (this.stagesPlotsOrigin + 4)
                    this.stagesPlotsOrigin = this.stagesPlotsOrigin + 1;
                end
                
                this.refreshSelectedStage();
                this.refreshStagesBar(false);
                this.refreshDynamicRange();
            end
        end
        
        function this = deleteStageSelect(this, src, event)
%             disp('holu')
            if ~isempty(this.stagesList)
                if ~isempty(this.stagesList{this.selectedStage}.poles)
                    indP = find(this.polesUsed == this.stagesList{this.selectedStage}.poles(imag(this.stagesList{this.selectedStage}.poles)>=0));
                    this.polesDisp(end+1) = this.polesUsed(indP);
                    this.polesUsed(indP) = [];   
                    if ~isempty(this.stagesList{this.selectedStage}.zeros)
                        for i = 1:length(this.stagesList{this.selectedStage}.zeros(imag(this.stagesList{this.selectedStage}.zeros)>=0))
                            indZ = find(this.zerosUsed == this.stagesList{this.selectedStage}.zeros(i), 1);
                            this.zerosDisp(end+1) = this.zerosUsed(indZ);
                            this.zerosUsed(indZ) = [];
                        end
                    end
                end
%             disp('holu2')
                this.stagesList{:,this.selectedStage} = [];
%             disp('holu5')
                this.stagesList = this.stagesList(~cellfun('isempty', this.stagesList));
%             disp('holu4')
                if this.selectedStage > length(this.stagesList)
                    this.selectedStage = this.selectedStage - 1;
                    if length(this.stagesList) > 5
                        this.stagesPlotsOrigin = this.stagesPlotsOrigin - 1;
                    end
                end
%             disp('holu3')
                this.refreshPolesZerosList();
                this.refreshStagesBar(false);
                this.calcTotalAttenuation();
                this.refreshSelectedStage();
                this.refreshDynamicRange();
            end
        end
        
        function this = StageSelect(this, ~, ~)
            if ~isempty(this.stagesList)
                indP = find(this.polesUsed == this.stagesList{this.selectedStage}.poles(2));
                this.polesDisp(end+1) = this.polesUsed(indP);
                this.polesUsed(indP) = [];   
                if ~isempty(this.stagesList{this.selectedStage}.zeros)
                    indZ = find(this.zerosUsed == this.stagesList{this.selectedStage}.zeros(2));
                    this.zerosDisp(end+1) = this.zerosUsed(indZ);
                    this.zerosUsed(indZ) = [];
                end
                
                this.stagesList{:,this.selectedStage} = [];
                this.stagesList = this.stagesList(~cellfun('isempty', this.stagesList));
                
                if this.selectedStage > length(this.stagesList)
                    this.selectedStage = this.selectedStage - 1;
                    if length(this.stagesList) > 5
                        this.stagesPlotsOrigin = this.stagesPlotsOrigin - 1;
                    end
                end
                
                this.refreshPolesZerosList();
                this.refreshStagesBar(false);
                this.calcTotalAttenuation();
                this.refreshSelectedStage();
            end
        end
        
        function this = stage1Select(this, ~, ~)
            this.selectedStage = this.stagesPlotsOrigin;
            this.refreshSelectedStage();
        end
        
        function this = stage2Select(this, ~, ~)
            this.selectedStage = this.stagesPlotsOrigin + 1;
            this.refreshSelectedStage();
        end
        
        function this = stage3Select(this, ~, ~)
            this.selectedStage = this.stagesPlotsOrigin + 2;
            this.refreshSelectedStage();
        end
        
        function this = stage4Select(this, ~, ~)
            this.selectedStage = this.stagesPlotsOrigin + 3;
            this.refreshSelectedStage();
        end
        
        function this = stage5Select(this, ~, ~)
            this.selectedStage = this.stagesPlotsOrigin + 4;
            this.refreshSelectedStage();
        end
        
        function this = listSelect(this, ~, ~)
            this.plotPolesZeros();
        end
        
        function plotPolesZeros(this)
            hP = this.handles.poleZeroAxes;
            cla(hP);
            hold(hP, 'off');
            PSelV = get(this.handles.polesList, 'Value');
            totalPoles = [this.polesDisp' this.polesUsed']';
            if ~isempty(PSelV)
                PSel = totalPoles(PSelV);
            else
                PSel = [];
            end
            
            ZSelV = get(this.handles.zerosList, 'Value');
            if ~isempty(ZSelV)
                totalZeros = [this.zerosDisp' this.zerosUsed']';
                ZSelV2 = [ZSelV(ZSelV <= length(this.zerosDisp)) (ZSelV(ZSelV > (length(this.zerosDisp)+1)))-1];
                ZSel = totalZeros(ZSelV2);
            else
                ZSel = [];
            end

            poleZeroMap(hP, allPoles(this.polesUsed), allZeros(this.zerosUsed), allPoles(this.polesDisp), allZeros(this.zerosDisp), allPoles(PSel), allZeros(ZSel), [0 0 1], [0 1 0], true);
            xlabel(hP, '\sigma Real Frequency [rad/s]','FontSize', 11);
            ylabel(hP, 'j\omega Imaginary Frequency [rad/s]', 'FontSize', 11);
        end
        
        function suitGains(this)
            this.calcTotalAttenuation();
            auxTransfer = 1;
            for i = 1:length(this.stagesList)
                auxTransfer = this.stagesList{i}.transferFunction*auxTransfer;
            end
            [tempAttenuation ~] = bode(auxTransfer, this.stagesList{i}.freq*(2*pi));
            minAttenuation = max(tempAttenuation);
            gainAdj = nthroot(minAttenuation/(10^(this.filterData.gain/20)), length(this.stagesList));
            
            for k = 1:length(this.stagesList)
                this.stagesList{k}.transferFunction = this.stagesList{k}.transferFunction/gainAdj;
                [this.stagesList{k}.attenuation ~] = bode(this.stagesList{k}.transferFunction, this.stagesList{k}.freq*(2*pi));
                this.stagesList{k}.attenuation = -20*log10(squeeze(this.stagesList{k}.attenuation));
                this.stagesList{k}.Gi = this.stagesList{k}.Gi/gainAdj;
            end
            this.calcTotalAttenuation();
                
        end
        
        function refreshPolesZerosList(this)

            this.sortLists();
            
            set(this.handles.polesList, 'String', '');
            set(this.handles.polesList, 'Value', 1);
            
            if strcmp(this.freqUnits, 'Hz')
                den = (2*pi);
            else
                den = 1;
            end

            for i = 1:length(this.polesDisp)
                listStr = strcat(sprintf('%.2f', abs(this.polesDisp(i))/den), {' '}, this.freqUnits, {' - Q = '}, sprintf('%.2f', pole2Q(this.polesDisp(i))));
                set(this.handles.polesList, 'String', [get(this.handles.polesList, 'String'); listStr]);
            end
            for i = 1:length(this.polesUsed)
                listStr = strcat(sprintf('%.2f', abs(this.polesUsed(i))/den), {' '}, this.freqUnits, {' - Q = '}, sprintf('%.2f', pole2Q(this.polesUsed(i))), {' (Used)'});
                set(this.handles.polesList, 'String', [get(this.handles.polesList, 'String'); listStr]);
            end
            

            set(this.handles.zerosList, 'String', '');
            set(this.handles.zerosList, 'Value', 1);
            for i = 1:length(this.zerosDisp)
                listStr = strcat(sprintf('%.2f', abs(this.zerosDisp(i))/den), {' '}, this.freqUnits);
                set(this.handles.zerosList, 'String', [get(this.handles.zerosList, 'String'); listStr]);
            end

            set(this.handles.zerosList, 'String', [get(this.handles.zerosList, 'String'); 'None']);
            for i = 1:length(this.zerosUsed)
                listStr = strcat(sprintf('%.2f', abs(this.zerosUsed(i))/den), {' '}, this.freqUnits, {' (Used)'});
                set(this.handles.zerosList, 'String', [get(this.handles.zerosList, 'String'); listStr]);
            end

            this.plotPolesZeros();

        end
        
        function sortLists(this)
            this.polesDisp = esort(this.polesDisp);
            this.zerosDisp = esort(this.zerosDisp);
            this.polesUsed = esort(this.polesUsed);
            this.zerosUsed = esort(this.zerosUsed);
        end
        
        function calcTotalAttenuation(this)
            if ~isempty(this.stagesList)
                this.totalAttenuation = [];
                totalTransferFunction = 1;
                for i = 1:length(this.stagesList)
                    totalTransferFunction = totalTransferFunction * this.stagesList{i}.transferFunction;
                end
                [this.totalAttenuation ~] = bode(totalTransferFunction, this.stagesList{1}.freq*(2*pi));
                this.totalAttenuation = -20*log10(squeeze(this.totalAttenuation));
            end
        end
        
        function refreshDynamicRange(this)
            if isempty(this.polesDisp) && isempty(this.zerosDisp)
                vectorNum = zeros(length(this.stagesList), 1);
                for i = 1:length(this.stagesList)
                    vectorTF(i) = this.stagesList{i}.transferFunction;
                    vectorNum(i) = i;
                end
                [DR min max] = dinrange(vectorTF, this.passRange, vectorNum);
                set(this.handles.TDR, 'String', num2str(round((20*log10(DR))*100)/100));
                set(this.handles.dB3, 'Visible', 'On');
            else
                set(this.handles.TDR, 'String', 'N/A');
                set(this.handles.dB3, 'Visible', 'Off');
            end            
        end
        
    end
end

function Q = pole2Q(pole)
    if length(pole) > 1
        wp = sqrt(pole(1)*pole(2));
        Q = abs(wp/(pole(1)+pole(2)));
    else
        [alpha ~] = cart2pol(real(pole), imag(pole));
        Q = abs(1./(2*sin(abs(alpha-pi/2))));
    end
end

function polesRet = allPoles(poles)
    polesRet = [poles conj(poles)];
    polesRet = unique(polesRet);
end

function zerosRet = allZeros(zeros)
    zerosRet = [zeros' conj(zeros(imag(zeros)~=0))']';
end

function removeAxis(sP)
    set(sP, 'XTickLabelMode', 'Manual');
    set(sP, 'XTick', []);
    set(sP, 'YTickLabelMode', 'Manual');
    set(sP, 'YTick', []);
end